

GUI_RAM_CODE
bool lcd_write_completed(void)
{
    if( lcd_wrctl.block_write ) 
    {
        if( lcd_wrctl.offset < lcd_wrctl.size )
        {
            uint16_t remain = lcd_wrctl.size - lcd_wrctl.offset;
            if( remain > LCD_XFER_MAX_SIZE )
            {
                remain = LCD_XFER_MAX_SIZE;
            }
            LCD_SEND_DATA_ASYNC( lcd_wrctl.pdata + lcd_wrctl.offset, remain );
            lcd_wrctl.offset += remain;
        }
        else 
        {
            lcd_wrctl.blocks.used ++;
            if( lcd_wrctl.blocks.used < lcd_wrctl.blocks.count )
            {
                // check next row 
                lcd_wrctl.pdata += lcd_wrctl.blocks.offset; // next block  
                lcd_wrctl.offset = 0; 
                uint16_t remain = lcd_wrctl.size;
                if( remain > LCD_XFER_MAX_SIZE )
                {
                    remain = LCD_XFER_MAX_SIZE; 
                }
                
                LCD_SEND_DATA_ASYNC( lcd_wrctl.pdata, remain );
                lcd_wrctl.offset = remain; //start a block 
            }
            else 
            {
                lcd_wrctl.isbusy = false; 
            }
        }
    }
    else 
    {
        if( lcd_wrctl.offset < lcd_wrctl.size )
        {
            uint16_t remain = lcd_wrctl.size - lcd_wrctl.offset;
            if( remain > LCD_XFER_MAX_SIZE )
            {
                remain = LCD_XFER_MAX_SIZE;
            }
            LCD_SEND_DATA_ASYNC( lcd_wrctl.pdata + lcd_wrctl.offset, remain );
            lcd_wrctl.offset += remain;
        }
        else 
        {
            lcd_wrctl.isbusy = false; 
        }
    }
    return lcd_wrctl.isbusy; 
}



static void driver_init(void)
{
    LCD_SET_RESET;
    LCD_DELAY_MS(10);
    LCD_SET_nRESET;
    LCD_DELAY_MS( 10 );
    
    lcd_write_command( 0x11 ); 
    LCD_DELAY_MS( 100 );
    
    lcd_write_command( 0x36 ); // Memory Data Access Control  
    {
        uint8_t data[] = {0x00}; //horizonal
        lcd_write_data( data, sizeof(data) );
    }

    lcd_write_command( 0x3A );
    {
        uint8_t data[] = {0x05}; //16bits color
        lcd_write_data( data, sizeof(data) );
    }

    lcd_write_command( 0xB2 );
    {
        uint8_t data[] = {0x0C, 0x0C, 0x00, 0x33, 0x33}; 
        lcd_write_data( data, sizeof(data) );
    }

    lcd_write_command( 0xB7 );
    {
        uint8_t data[] = {0x35}; 
        lcd_write_data( data, sizeof(data) );
    }
    lcd_write_command( 0xBB );
    {
        uint8_t data[] = {0x32}; // 1.35V 
        lcd_write_data( data, sizeof(data) );
    }
    lcd_write_command( 0xC0 );
    {
        uint8_t data[] = {0x2C}; 
        lcd_write_data( data, sizeof(data) );
    }
    lcd_write_command( 0xC2 );
    {
        uint8_t data[] = {0x01}; 
        lcd_write_data( data, sizeof(data) );
    }
    lcd_write_command( 0xC3 );
    {
        uint8_t data[] = {0x19};
        lcd_write_data( data, sizeof(data) );
    }
    lcd_write_command( 0xC4 );
    {
        uint8_t data[] = {0x20}; 
        lcd_write_data( data, sizeof(data) );
    }    
    lcd_write_command( 0xC6 );
    {
        uint8_t data[] = {0x0F}; // 60Hz 
        lcd_write_data( data, sizeof(data) );
    }
    lcd_write_command( 0xD0 );
    {
        uint8_t data[] = {0xA4, 0xA1}; 
        lcd_write_data( data, sizeof(data) );
    }
    lcd_write_command( 0xE0 );
    {
       uint8_t data[] = {0xD0, 0x08, 0x0E, 0x09, 0x09, 0x05, 0x31, 0x33, 0x48, 0x17, 0x14, 0x15, 0x31, 0x34};
        lcd_write_data( data, sizeof(data) );
    }
    lcd_write_command( 0xE1 );
    {
        uint8_t data[] = {0xD0, 0x08, 0x0E, 0x09, 0x09, 0x15, 0x31, 0x33, 0x48, 0x17, 0x14, 0x15, 0x31, 0x34}; 
        lcd_write_data( data, sizeof(data) );
    }
    lcd_write_command( 0x0E );
    {
        uint8_t data[] = {0x00, 0x10}; 
        lcd_write_data( data, sizeof(data) );
    }
    lcd_write_command( 0x21 );
}


void em_lcd_init(void)
{
    if( !is_initialized )
    {
        is_initialized = true;
        lcd_common_init();
        driver_init();
    }
}

void em_lcd_power_off(void)
{
    lcd_write_command( 0x28 ); // turn off 
    lcd_write_command( 0x10 ); // sleep in 

    xDLPS_reset( lcd_dlps_index );
}

void em_lcd_power_on(void)
{
    lcd_gdma_init();
    lcd_write_command( 0x11 ); // sleep out 
    lcd_write_command( 0x29 ); // turn on   
}
void em_lcd_sleep_out(void)
{
    lcd_gdma_init();
    lcd_write_command( 0x11 ); 

    xDLPS_set( lcd_dlps_index );
}
void em_lcd_turn_on(void)
{
    lcd_write_command( 0x29 );
}
GUI_RAM_CODE
void em_lcd_set_window(int16_t x0, int16_t y0, int16_t w, int16_t h)
{
    uint16_t xstart = x0;
    uint16_t ystart = y0;
    uint16_t xend = xstart + w - 1;
    uint16_t yend = ystart + h - 1;
    
    // column address set
    lcd_write_command(0x2A); { // CASET
        uint8_t data[] = { (xstart >> 8) & 0xFF , xstart & 0xFF, (xend >> 8) & 0xFF, xend & 0xFF };
        lcd_write_data(data, sizeof(data));
    }
    // row address set
    lcd_write_command(0x2B); {// RASET
        uint8_t data[] = { (ystart >> 8) & 0xFF, ystart & 0xFF, (yend >> 8) & 0xFF, yend & 0xFF };
        lcd_write_data(data, sizeof(data));
    }
    // write to RAM
    lcd_write_command(0x2C); // RAMWR  
}
GUI_RAM_CODE
void em_lcd_write(uint8_t* p_buffer, uint32_t size)
{   
    if( lcd_wrctl.isbusy )
    {
        return;
    }
    
    lcd_wrctl.isbusy = true; 
    lcd_wrctl.block_write = false;
    lcd_wrctl.pdata = (uint8_t*)p_buffer;
    lcd_wrctl.offset = 0;
    lcd_wrctl.size = size;
    
    uint32_t chunk_remain = size; 
    if( chunk_remain > LCD_XFER_MAX_SIZE )
    {
        chunk_remain = LCD_XFER_MAX_SIZE; 
    }
    lcd_wrctl.offset = chunk_remain;
    LCD_SEND_DATA_ASYNC( lcd_wrctl.pdata, chunk_remain );
}

/*
    p_buffer -> LCD Memory 
    offset   -> next block offset 
    size     -> size of block  
    rows     -> block count 
*/
GUI_RAM_CODE
void em_lcd_write_block( uint8_t* p_buffer, int16_t offset, uint16_t size, int16_t count )
{
    if( lcd_wrctl.isbusy )
    {
        return;
    }
    
    lcd_wrctl.isbusy                = true;
    lcd_wrctl.block_write           = true; 
    lcd_wrctl.pdata                 = (uint8_t*)p_buffer;
    lcd_wrctl.offset                = 0; 
    lcd_wrctl.size                  = size; 
    
    lcd_wrctl.blocks.used           = 0;
    lcd_wrctl.blocks.size           = size;
    lcd_wrctl.blocks.count          = count;
    lcd_wrctl.blocks.offset         = offset;
    
    uint32_t chunk_remain = size; 
    if( chunk_remain > LCD_XFER_MAX_SIZE )
    {
        chunk_remain = LCD_XFER_MAX_SIZE; 
    }
    lcd_wrctl.offset                = chunk_remain; 
    LCD_SEND_DATA_ASYNC( lcd_wrctl.pdata, chunk_remain );
//    LOG_INFO1("[LCD]: block write count = %d", count );
}

void em_lcd_draw_point( int16_t x, int16_t y, uint16_t color )
{
    em_lcd_set_window(x, y, 1, 1 );
    LCD_SEND_DATA( (uint8_t*)&color, 2);
}

void em_lcd_wait_transfer_completed(void)
{
    uint32_t timestamp=em_rtc_get_ticks();
    while( lcd_wrctl.isbusy && !em_rtc_check_time_expired( timestamp, EM_RTC_TOTICKS(500) ));
}

bool em_lcd_check_transfer_completed(void)
{
    return lcd_wrctl.isbusy; 
}

